﻿using System;
using System.Xml;

namespace ValidationFramework.Extensions
{
    public static class XmlElementExtensions
    {
        /// <summary>
        /// Attempts to get and convert an attribute from a <see cref="XmlElement"/>. If the item does not exist or can't be converted a <paramref name="defaultValue"/> is returned.
        /// </summary>
        /// <typeparam name="T">The <see cref="Type"/> to try to convert to.</typeparam>
        /// <param name="element">The <see cref="XmlElement"/> to extract the attribute value from.</param>
        /// <param name="key">The key to use or the extraction</param>
        /// <param name="defaultValue">The default value if <paramref name="key"/> is not found.</param>
        /// <returns>The value from <paramref name="attributes"/> if <paramref name="key"/> exists; otherwise <paramref name="defaultValue"/>.</returns>
        internal static T GetAttribute<T>(this XmlElement element, string key, T defaultValue)
        {
            if (!element.HasAttribute(key))
                return defaultValue;

            var stringValue = element.GetAttribute(key);

            var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(T));

            if (!converter.CanConvertFrom(typeof(string)))
                return defaultValue;

            return (T)converter.ConvertFromString(stringValue);
        }

        /// <summary>
        /// weakly typed
        /// </summary>
        /// <param name="element"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        internal static object GetAttributeWithConversion(this XmlElement element, Type typeToConvertTo, string key)
        {
            if (!element.HasAttribute(key))
                throw new ArgumentOutOfRangeException(string.Format("The key '{0}' cannot be found in xml element.", key));

            var stringValue = element.GetAttribute(key);

            var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeToConvertTo);

            return converter.ConvertFromString(stringValue);
        }

        /// <summary>
        /// Attempts to get and cast attribute from a <see cref="XmlElement"/>. If the item does not exist or can't be casted exceptions are thrown.
        /// </summary>
        /// <typeparam name="T">The <see cref="Type"/> to try to convert to.</typeparam>
        /// <param name="element">The <see cref="XmlElement"/> to extract the attribute value from.</param>
        /// <param name="key">The key to use or the extraction</param>
        /// <param name="defaultValue">The default value if <paramref name="key"/> is not found.</param>
        /// <returns>The value from <paramref name="attributes"/> if <paramref name="key"/> exists; otherwise <paramref name="defaultValue"/>.</returns>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        internal static T GetAttribute<T>(this XmlElement element, string key)
        {
            if (!element.HasAttribute(key))
                throw new ArgumentOutOfRangeException(string.Format("The key '{0}' cannot be found in xml element.", key));

            var stringValue = element.GetAttribute(key);

            var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(T));

            return (T)converter.ConvertFromString(stringValue);
        }
    }
}
